#pragma once
#include <memory>
#include "CTimer.h"

class Process;
typedef std::shared_ptr<Process> StrongProcessPtr;
typedef std::weak_ptr<Process> WeakProcessPtr;

class Process
{
	friend class ProcessManager;
public:
	enum State
	{
		UNINITIALIZED = 0,
		REMOVED,
		RUNNING,
		PAUSED,
		SUCCEEDED,
		FAILED,
		ABORTED,
		EXPIRED,
	};

	Process();
	virtual ~Process();

protected:
	virtual void VInit() {m_state = RUNNING;}
	virtual void VUpdate(double deltaSecs) = 0;
	virtual void VOnSuccess() { }
	virtual void VOnFail() { }
	virtual void VOnAbort() { }

	//the process has to change to succeeded or failed here
	//otherwise the manager will change it to succeeded
	virtual void VOnExpire() { }

private:
	void SetState(State newState) { m_state = newState; }

protected:
	StrongProcessPtr m_pChild;
	CTimer m_timer;
	State m_state;
	double m_lifeTime; //in sec
	int m_refreshIntervall; //in ms
	double m_pauseRunTime;

public:
	inline void Succeed();
	inline void Fail();

	inline void Pause();
	inline void UnPause();

	State GetState() const { return m_state; }
	bool IsAlive(void) const { return (m_state == RUNNING || m_state == PAUSED); }
	bool IsDead(void) const { return (m_state == SUCCEEDED || m_state == FAILED 
		|| m_state == ABORTED || m_state == EXPIRED); }
	bool IsRemoved(void) const { return (m_state == REMOVED); }
	bool IsPaused(void) const { return m_state == PAUSED; }
	bool IsExpired();
	bool IsTimeToUpdate();

	inline void AttachChild(StrongProcessPtr pChild);
	StrongProcessPtr RemoveChild(void);  // releases ownership of the child
	StrongProcessPtr PeekChild(void) { return m_pChild; }  // doesn't release ownership of the child
};

inline void Process::Succeed(void)
{
	//GCC_ASSERT(m_state == RUNNING || m_state == PAUSED);
	m_state = SUCCEEDED;
}

inline void Process::Fail(void)
{
	//GCC_ASSERT(m_state == RUNNING || m_state == PAUSED);
	m_state = FAILED;
}

inline void Process::AttachChild(StrongProcessPtr pChild)
{
	if (m_pChild)
		m_pChild->AttachChild(pChild);
	else
		m_pChild = pChild;
}

inline void Process::Pause(void)
{
	if (m_state == RUNNING)
	{
		m_state = PAUSED;
		m_pauseRunTime = m_timer.getRunTime();
		m_timer.reset();
	}
	else
	{
		//GCC_WARNING("Attempting to pause a process that isn't running");
	}
}

inline void Process::UnPause(void)
{
	if (m_state == PAUSED)
	{
		m_state = RUNNING;
		m_timer.reset();
		m_timer.resetRunTime(m_pauseRunTime);
		m_pauseRunTime = 0;
	}
	else
	{
		//GCC_WARNING("Attempting to unpause a process that isn't paused");
	}
}